using System;
using System.Diagnostics;

using org.fourzoas.noise;

namespace org.fourzoas.noise.module {
  /// <summary>
  /// Abstract base class for noise modules
  /// <para>
  /// A <em>noise module</em> is an object that calculates and outputs a value given a three-dimensional input value.
  /// </para>
  /// </summary>
  /// <remarks>
  /// <para>
  /// Each type of noise module uses a specific method to calculate an output value.
  /// Some of these methods include:
  /// <list type="bullet">
  /// <item><description>Calculating a value using a coherent-noise function or some other mathematical function.</description></item>
  /// <item><description>Mathematically changing the output value from another noise module in various ways.</description></item>
  /// <item><description>Combining the output values from two noise modules in various ways.</description></item>
  /// </list>
  /// </para>
  /// <para>
  /// An application can use the output values from these noise modules in the following ways:
  /// <list type="bullet">
  /// <item><description>It can be used as an elevation value for a terrain height map.</description></item>
  /// <item><description>It can be used as a grayscale (or an RGB-channel) value for a procedural texture.</description></item>
  /// <item><description>It can be used as a position value for controlling the movement of a simulated lifeform.</description></item>
  /// </list>
  /// </para>
  /// <para>
  /// A noise module defines a near-infinite 3-dimensional texture.  Each position in this "texture" has a specific value.
  /// </para>
  /// <h5>Combining noise modules</h5>
  /// <para>
  /// Noise modules can be combined with other noise modules to generate complex output values.  A noise module that is used
  /// as a source of output values for another noise module is called a <em>source module</em>.  Each of these source modules
  /// may be connected to other source modules, and so on.
  /// </para>
  /// <para>
  /// There is no limit to the number of noise modules that can be connected together in this way.  However, each connected
  /// noise module increase the time required to calculate an output value.
  /// </para>
  /// <h5>Noise-module categories</h5>
  /// <para>The noise module classes that are included in org.4zoas.noise can be roughly divided into five categories</para>
  /// <h6>Generator Modules</h6>
  /// <para>
  /// A generator module outputs a value generated by a coherent-noise
  /// function or some other mathematical function.
  ///</para>
  /// <para>
  /// Examples of generator modules include:
  /// <list type="bullet">
  /// <item><description><see cref="Const"/>: Outputs a constant value.</description></item>
  /// <item><description><see cref="Perlin"/>: Outputs a value generated by a Perlin-noise function.</description></item>
  /// <item><description><see cref="Voronoi"/>: Outputs a value generated by a Voronoi-cell function.</description></item>
  /// </list>
  ///</para>
  /// <h6>Modifier Modules</h6>
  ///<para>
  /// A modifer module mathematically modifies the output value from a
  /// source module.
  ///</para>
  /// <para>
  /// Examples of modifier modules include:
  /// <list type="bullet">
  /// <item><description><see cref="Curve"/>: Maps the output value from the source module onto an arbitrary function curve.</description></item>
  /// <item><description><see cref="Invert"/>: Inverts the output value from the source module.</description></item>
  /// </list>
  ///</para>
  /// <h6>Combiner Modules</h6>
  ///<para>
  /// A combiner module mathematically combines the output values from two
  /// or more source modules together.
  ///</para>
  /// <para>
  /// Examples of combiner modules include:
  /// <list type="bullet">
  /// <item><description><see cref="Add"/>: Adds the two output values from two source modules.</description></item>
  /// <item><description><see cref="Max"/>: Outputs the larger of the two output values from two source modules.</description></item>
  /// </list>
  ///</para>
  /// <h6>Selector Modules</h6>
  ///<para>
  /// A selector module uses the output value from a <i>control module</i>
  /// to specify how to combine the output values from its source modules.
  ///</para>
  /// <para>
  /// Examples of selector modules include:
  /// <list type="bullet">
  /// <item><description><see cref="Blend"/>: Outputs a value that is linearly interpolated between the output values from two source modules; the interpolation weight is determined by the output value from the control module.</description></item>
  /// <item><description><see cref="Select"/>: Outputs the value selected from one of two source modules chosen by the output value from a control module.</description></item>
  /// </list>
  ///</para>
  /// <h6>Transformer Modules</h6>
  ///<para>
  /// A transformer module applies a transformation to the coordinates of
  /// the input value before retrieving the output value from the source
  /// module.  A transformer module does not modify the output value.
  ///</para>
  /// <para>
  /// Examples of transformer modules include:
  /// <list type="bullet">
  /// <item><description><see cref="RotatePoint"/>: Rotates the coordinates of the input value around the origin before retrieving the output value from the source module.</description></item>
  /// <item><description><see cref="ScalePoint"/>: Multiplies each coordinate of the input value by a constant value before retrieving the output value from the source module.</description></item>
  /// </list>
  ///</para>
  /// <h5>Connecting source modules to a noise module</h5>
  ///<para>
  /// An application connects a source module to a noise module by passing
  /// the source module to the SetSourceModule() method.
  ///</para>
  /// <para>
  /// The application must also pass an <em>index value</em> to
  /// <c><see cref="SetSourceModule"/></c> as well.  An index value is a numeric identifier for
  /// that source module.  Index values are consecutively numbered starting
  /// at zero.
  ///</para>
  /// <para>
  /// To retrieve a reference to a source module, pass its index value to
  /// the <c><see cref="GetSourceModule"/></c> method.
  ///</para>
  /// <para>
  /// Each noise module requires the attachment of a certain number of
  /// source modules before it can output a value.  For example, the
  /// <see cref="Add"/> module requires two source modules, while the
  /// <see cref="Perlin"/> module requires none.  Call the
  /// <c><see cref="GetSourceModuleCount"/></c> method to retrieve the number of source modules
  /// required by that module.
  ///</para>
  /// <para>
  /// For non-selector modules, it usually does not matter which index value
  /// an application assigns to a particular source module, but for selector
  /// modules, the purpose of a source module is defined by its index value.
  /// For example, consider the <see cref="Select"/> noise module, which
  /// requires three source modules.  The control module is the source
  /// module assigned an index value of 2.  The control module determines
  /// whether the noise module will output the value from the source module
  /// assigned an index value of 0 or the output value from the source
  /// module assigned an index value of 1.
  ///</para>
  /// <h5>Generating output values with a noise module</h5>
  /// <para>
  /// Once an application has connected all required source modules to a
  /// noise module, the application can now begin to generate output values
  /// with that noise module.
  ///</para>
  /// <para>
  /// To generate an output value, pass the (<em>x</em>, <em>y</em>, <em>z</em>) coordinates
  /// of an input value to the <c><see cref="GetValue"/></c> method.
  ///</para>
  /// <h5>Using a noise module to generate terrain height maps or textures</h5>
  /// <para>
  /// One way to generate a terrain height map or a texture is to first
  /// allocate a 2-dimensional array of floating-point values.  For each
  /// array element, pass the array subscripts as <em>x</em> and <em>y</em> coordinates
  /// to the <c><see cref="GetValue"/></c> method (leaving the <em>z</em> coordinate set to zero) and
  /// place the resulting output value into the array element.
  ///</para>
  /// <h5>Creating your own noise modules</h5>
  /// <para>
  /// Create a class that publicly derives from <see cref="Module"/>.
  ///</para>
  /// <para>
  /// In the constructor, call the base class' constructor while passing the
  /// return value from <c><see cref="GetSourceModuleCount"/></c> to it.
  ///</para>
  /// <para>
  /// Override the <c><see cref="GetSourceModuleCount"/></c> pure virtual method.  From this
  /// method, return the number of source modules required by your noise
  /// module.
  ///</para>
  /// <para>
  /// Override the <c><see cref="GetValue"/></c> pure virtual method.  For generator modules,
  /// calculate and output a value given the coordinates of the input value.
  /// For other modules, retrieve the output values from each source module
  /// referenced in the protected <c><see cref="m_sourceModule"/></c> array, mathematically
  /// combine those values, and return the combined value.
  ///</para>
  /// <para>
  /// When developing a noise module, you must ensure that your noise module
  /// does not modify any source module or control module connected to it; a
  /// noise module can only modify the output value from those source
  /// modules.  You must also ensure that if an application fails to connect
  /// all required source modules via the <c><see cref="SetSourceModule"/></c> method and then
  /// attempts to call the <c><see cref="GetValue"/></c> method, your module will raise an
  /// assertion.
  ///</para>
  /// <para>
  /// It shouldn't be too difficult to create your own noise module.  If you
  /// still have some problems, take a look at the source code for
  /// <see cref="Add"/>, which is a very simple noise module.
  /// </para>
  /// </remarks>
  public abstract class Module {
    /// <summary>
    /// Default constructor.
    /// </summary>
    protected Module() {
      m_sourceModule = new ModuleList();
    }
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="sourceModuleCount">Number of source modules this module takes</param>
    public Module(int sourceModuleCount) {
      m_sourceModule = new ModuleList(sourceModuleCount);
    }

    /// <summary>
    /// Destructor.  Makes sure source modules are discarded
    /// </summary>
    ~Module() {
      m_sourceModule = null;
    }

    /// <summary>
    /// <strong>Obsolete.</strong> Returns a reference to a source module connected to this noise module.
    /// </summary>
    /// <param name="index">The index value assigned to the source module.</param>
    /// <returns>A reference to the source module</returns>
    /// <remarks>
    /// <h5>Preconditions</h5>
    /// <para>The index value ranges from 0 to one less than the number of source modules required by this noise module.</para>
    /// <para>A source module with the specified index value has been added to this noise module via a call to <c><see cref="SetSourceModule"/></c></para>
    /// <h5>Obsolete</h5>
    /// <para>
    /// This function is obsolete and is included for legacy use.  It has been replaced by <see cref="SourceModules"/>.</para>
    /// <code>Module m = module.GetSourceModule(n);</code>
    /// is now equivalent to:
    /// <code>Module m = module.SourceModules[n];</code>
    /// </remarks>
    /// <exception cref="NoModuleException">
    /// <para>
    /// See the preconditions for more information.
    /// </para>
    /// <para>
    /// Each noise module requires the attachment of a certain number of source modules before an application can call the
    /// <c><see cref="GetValue"/></c> method.
    /// </para>
    /// </exception>
    public virtual Module GetSourceModule(int index) {
      //Debug.Assert(m_sourceModule != null);

      //if (index >= GetSourceModuleCount() || index < 0 || m_sourceModule[index] == null)
      //  throw new NoModuleException();
      return m_sourceModule[index];
    }

    /// <summary>
    /// Returns the number of source modules required by this noise module.
    /// </summary>
    /// <returns>The number of source modules required by this noise module.</returns>
    public abstract int GetSourceModuleCount();

    /// <summary>
    /// Generates an output value given the coordinates of the specified input value.
    /// </summary>
    /// <param name="x">The <em>x</em> coordinate of the input value.</param>
    /// <param name="y">The <em>y</em> coordinate of the input value.</param>
    /// <param name="z">The <em>z</em> coordinate of the input value.</param>
    /// <returns>The output value.</returns>
    /// <remarks>
    /// <para>
    /// Before an application can call this method, it must first connect all required source modules via the
    /// <c><see cref="SetSourceModule"/></c> method.  If these source modules are not connected to this noise module,
    /// this method raises a debug assertion.
    /// </para>
    /// <para>
    /// To determine the number of source modules required by this noise module, call the 
    /// <c><see cref="GetSourceModuleCount"/></c> method.
    /// </para>
    /// <h5>Preconditions</h5>
    /// <para>
    /// All source modules required by this noise module have been passed to the <c><see cref="SetSourceModule"/></c> method.
    /// </para>
    /// </remarks>
    public abstract double GetValue(double x, double y, double z);

    /// <summary>
    /// <strong>Obsolete.</strong> Connects a source module to this noise module.
    /// </summary>
    /// <param name="index">An index value to assign to this source module.</param>
    /// <param name="sourceModule">The source module to attach.</param>
    /// <exception cref="ArgumentOutOfRangeException">
    /// An invalid parameter was specified; see the preconditions for more information.
    /// </exception>
    /// <remarks>
    /// <para>
    /// A noise module mathematically combines the output values from the source modules to generate the value
    /// returned by <c><see cref="GetValue"/></c>.
    /// </para>
    /// <para>
    /// The index value to assign a source module is a unique identifier for that source module.
    /// If an index value has already been assigned to a source module, this noise module replaces the old
    /// source module with the new source module.
    /// </para>
    /// <para>
    /// Before an application can call the <c><see cref="GetValue"/></c> method, it must first connect all
    /// required source modules.  To determine the number of source modules required by this noise module, call
    /// the <c><see cref="GetSourceModuleCount"/></c> method.
    /// </para>
    /// <para>
    /// This source module must exist throughout the lifetime of this noise module unless another source
    /// module replaces that source module.
    /// </para>
    /// <para>
    /// A noise module does not modify a source module; it only modifies its output values.
    /// </para>
    /// <h5>Preconditions</h5>
    /// <para>
    /// The index value ranges for 0 to one less than the number of source modules required by this
    /// noise module.
    /// </para>
    /// <h5>Obsolete</h5>
    /// <para>
    /// This function is obsolete and is included for legacy use.  It has been replaced by <see cref="SourceModules"/>.</para>
    /// <para>
    /// <code>module.SetSourceModule(n, sourceModule);</code>
    /// </para>
    /// <para>is now equivalent to:</para>
    /// <para>
    /// <code>module.SourceModules[n] = sourceModule;</code>
    /// </para>
    /// </remarks>
    virtual public void SetSourceModule(int index, Module sourceModule) {
      //Debug.Assert(m_sourceModule != null);
      //if (index >= GetSourceModuleCount() || index < 0)
      //  throw new ArgumentOutOfRangeException("index");
      m_sourceModule[index] = sourceModule;
    }

    /// <summary>
    /// An array containing the source modules required by this noise module
    /// </summary>
    protected ModuleList m_sourceModule;
    /// <summary>
    /// A readonly property giving access to the list of modules.
    /// </summary>
    public virtual ModuleList SourceModules {
      get {
        return m_sourceModule;
      }
    }
  }
}
